package code

import (
	"encoding/json"
	"fmt"
	"strconv"
	"sync"
)

var (
	container *Message
	onceCode  sync.Once
)

func init() {
	onceCode.Do(func() {
		container = newMessage()
	})
}

type ICode interface {
	Register() *Message
	Message(variables ...interface{}) string
}

type Message struct {
	store sync.Map
}

func newMessage() *Message {
	return &Message{store: sync.Map{}}
}

func (this *Message) Len() int {
	length := 0
	this.store.Range(func(key, value any) bool {
		length++
		return true
	})
	return length
}

func (this *Message) Put(key ICode, value string) *Message {
	if _, ok := this.store.Load(key); ok {
		panic(fmt.Sprintf("Code %d Duplicate Definition", key))
	}
	this.store.Store(key, value)
	return this
}

func (this *Message) Get(key ICode) string {
	if s, ok := this.store.Load(key); ok {
		return s.(string)
	}
	return "Undefined"
}

func (this *Message) Exist(key ICode) bool {
	_, ok := this.store.Load(key)
	return ok
}

func (this *Message) ForEach(callable func(key ICode, value string) bool) {
	this.store.Range(func(key, value any) bool {
		return callable(key.(ICode), value.(string))
	})
}

func (this *Message) Merge(m *Message) {
	m.ForEach(func(key ICode, value string) bool {
		this.Put(key, value)
		return true
	})
}

func Get(ic ICode, variables ...interface{}) string {
	if !container.Exist(ic) {
		ic.Register()
	}
	return format(container.Get(ic), variables...)
}

func format(format string, variables ...interface{}) string {
	if len(variables) > 0 {
		return fmt.Sprintf(format, variables...)
	}
	return format
}

func Container() *Message {
	return container
}

type CodeMessage struct {
	Code    int64  `json:"code"`
	Message string `json:"message"`
}

func New(ic ICode) *CodeMessage {
	i64, err := strconv.ParseInt(fmt.Sprintf("%d", ic), 10, 64)
	if nil != err {
		panic(err)
	}
	return &CodeMessage{Code: i64, Message: ic.Message()}
}

func (this *CodeMessage) Json() string {
	js, err := json.Marshal(this)
	if err != nil {
		panic(err)
	}
	return string(js)
}
